﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.ComponentModel;
using wyDay.Controls;

namespace SlideshowGenerator
{
    public class Slideshow
    {
        /// <summary>
        /// Contains names of the columns used in the SlideshowImages-Table.
        /// </summary>
        public class TableColumnNames
        {
            public const string ID = "ID";
            public const string CaptionImages = "CaptionImages";
            public const string CaptionImageFrequency = "CaptionImageFrequency";
            public const string NumberOfImages = "NumberOfImages";
        }

        /// <summary>
        /// DataTable, which contains all slideshow elements.
        /// </summary>
        DataTable _dt_slideshowImages;

        /// <summary>
        /// ID DataColumn of DataTable SlideshowImages
        /// </summary>
        DataColumn _dc_id;

        /// <summary>
        /// Caption Image DataColumn of DataTable SlideshowImages
        /// </summary>
        DataColumn _dc_capImg;

        /// <summary>
        /// Caption Image Frequency DataColumn of DataTable SlideshowImages
        /// </summary>
        DataColumn _dc_capImgFreq;

        /// <summary>
        /// Image Number DataColumn of DataTable SlideshowImages
        /// </summary>
        DataColumn _dc_imgnum;

        /// <summary>
        /// Gets the DataTable, which contains all Caption Images and Slideshow Images.
        /// </summary>
        public DataTable SlideshowImagesTable
        {
            get { return _dt_slideshowImages; }
        }

        private SortedList<int, SlideshowElement> _data;

        /// <summary>
        /// Gets all elements of the Slideshow with their IDs.
        /// </summary>
        public SortedList<int, SlideshowElement> Data
        {
            get { return _data; }
        }

        string _outputPath = String.Empty;

        /// <summary>
        /// Output Path for the Slideshow.
        /// </summary>
        public string OutputPath
        {
            get { return _outputPath; }
            set { _outputPath = value; }
        }

        string _slideShowPath = String.Empty;

        /// <summary>
        /// Gets the full path to the slideshow.
        /// </summary>
        public string SlideShowPath
        {
            get { return _slideShowPath; }
        }

        private int _imagesCount = 0;

        public int ImagesCount
        {
            get
            {
                int i = 0;
                foreach (SlideshowElement ssel in _data.Values)
                {
                    i += ssel.SlideshowImages.Count;
                    i += ssel.CaptionImages.Count; //Minimum
                    if (ssel.CaptionImageFrequency > 0) //plus the frequency of caption images
                    {
                        i += ssel.CaptionImages.Count * (ssel.SlideshowImages.Count / ssel.CaptionImageFrequency);
                    }
                }
                _imagesCount = i;
                return _imagesCount;
            }
        }

        /// <summary>
        /// Indicates whether the slideshow should be created with a progress view.
        /// </summary>
        private bool _createWithProgress = true;

        /// <summary>
        /// Gets or sets the current progress.
        /// </summary>
        private Windows7ProgressBar _progressBar = null;

        private BackgroundWorker _creator = new BackgroundWorker();

        /// <summary>
        /// Create new Slideshow-Object.
        /// </summary>
        public Slideshow()
        {
            //Create DataTable structure
            CreateTableStructure();

            //Create new Instance of Data-Dictionary
            _data = new SortedList<int, SlideshowElement>();
        }

        /// <summary>
        /// Creates the structure of the SlideshowImagesTable.
        /// </summary>
        private void CreateTableStructure()
        {
            _dt_slideshowImages = new DataTable("SlideshowImagesTable");

            _dc_id = new DataColumn(TableColumnNames.ID, typeof(int));
            _dc_capImg = new DataColumn(TableColumnNames.CaptionImages, typeof(string));
            _dc_capImgFreq = new DataColumn(TableColumnNames.CaptionImageFrequency, typeof(int));
            _dc_imgnum = new DataColumn(TableColumnNames.NumberOfImages, typeof(int));

            _dt_slideshowImages.Columns.Add(_dc_id);
            _dt_slideshowImages.Columns.Add(_dc_capImg);
            _dt_slideshowImages.Columns.Add(_dc_capImgFreq);
            _dt_slideshowImages.Columns.Add(_dc_imgnum);
        }

        /// <summary>
        /// Adds images to the slideshow.
        /// </summary>
        /// <param name="CaptionImagesPath">Caption image of the slideshow.</param>
        /// <param name="SlideshowImagesPaths">Images of the slideshow, if empty then null.</param>
        /// <returns>True, if images were added successfully.</returns>
        public bool AddImages(List<string> CaptionImagesPath, List<string> SlideshowImagesPaths)
        {
            bool success = true;
            string captionImagePathsString = String.Empty;
            foreach (string file in CaptionImagesPath)
            {
                if (!File.Exists(file))
                {
                    success = false;
                }
                captionImagePathsString = String.Format("{0}\"{1}\" ", captionImagePathsString, file);
            }
            if (success == true)
            {
                int sselid = _data.Count != 0 ? _data.Keys.Max() + 1 : 1;

                _data.Add(sselid, new SlideshowElement(CaptionImagesPath, SlideshowImagesPaths));
                _dt_slideshowImages.Rows.Add(sselid, captionImagePathsString, Configuration.CaptionImageFrequency, SlideshowImagesPaths.Count);
                success = true;
            }
            return success;
        }

        /// <summary>
        /// Changes the Caption Image Frequency of a slideshow element to a specific value.
        /// </summary>
        /// <param name="slideshowElementKey">Key of the slideshow element which is going to be edited.</param>
        /// <param name="saptionImageFrequency">New Caption Image Frequency of the slideshow element.</param>
        public void ChangeCaptionImageFrequency(int slideshowElementKey, int captionImageFrequency)
        {
            _data[slideshowElementKey].CaptionImageFrequency = captionImageFrequency;
        }

        /// <summary>
        /// Deletes slideshow images which are selected by the user.
        /// </summary>
        /// <param name="RowsToDelete">Rows which are selected by the user to delete.</param>
        public void DeleteSelectedImages(List<int> RowsToDelete)
        {
            foreach (int i in RowsToDelete)
            {
                _data.Remove(i + 1);
                _dt_slideshowImages.Rows[i].Delete();
            }
        }

        /// <summary>
        /// Creates the Slideshow out of the images in the SlideshowImagesTable.
        /// </summary>
        /// <returns>True, if the slideshow creation was ended successfully.</returns>
        public bool CreateSlideShow()
        {
            _createWithProgress = false;
            bool success = CreateSlideShow(null);
            _createWithProgress = true;
            return success;
        }

        /// <summary>
        /// Creates Slideshow with Progressbar View.
        /// </summary>
        /// <param name="CreationProgress">The progressbar in which the progress of the creation will be displayed.</param>
        /// <returns>True, if the slideshow creation was ended successfully.</returns>
        public bool CreateSlideShow(Windows7ProgressBar CreationProgress)
        {
            try
            {
                if (_createWithProgress)
                {
                    _progressBar = CreationProgress;
                }

                //Create Slideshow Folder
                int folderNumber = 1;
                while (Directory.Exists(String.Format("{0}\\Slideshow{1}", _outputPath, folderNumber)))
                {
                    folderNumber++;
                }
                DirectoryInfo dirinfo = Directory.CreateDirectory(String.Format("{0}\\Slideshow{1}", _outputPath, folderNumber));
                _slideShowPath = dirinfo.FullName;

                //Create Slideshow in folder
                int imgCount = 1;
                foreach (SlideshowElement ssel in _data.Values)
                {
                    //Create first caption images (this is the minimum)
                    string destFileName;
                    foreach (string capimg in ssel.CaptionImages)
                    {
                        destFileName = imgCount.ToString();
                        while (destFileName.Length < 5) destFileName = destFileName.Insert(0, "0");
                        CopyImageInSize(capimg, String.Format("{0}\\{1}.jpg", dirinfo.FullName, destFileName));
                        imgCount++;
                    }

                    int seriesCount = 0;
                    foreach (string img in ssel.SlideshowImages)
                    {
                        seriesCount++;

                        destFileName = imgCount.ToString();
                        while (destFileName.Length < 5) destFileName = destFileName.Insert(0, "0");
                        CopyImageInSize(img, String.Format("{0}\\{1}.jpg", dirinfo.FullName, destFileName));

                        imgCount++;

                        if (ssel.CaptionImageFrequency > 0) //if not wished to create just one time
                        {
                            if (seriesCount % ssel.CaptionImageFrequency == 0)
                            {
                                foreach (string capimg in ssel.CaptionImages)
                                {
                                    destFileName = imgCount.ToString();
                                    while (destFileName.Length < 5) destFileName = destFileName.Insert(0, "0");
                                    CopyImageInSize(capimg, String.Format("{0}\\{1}.jpg", dirinfo.FullName, destFileName));

                                    imgCount++;
                                }
                            }
                        }
                    }
                }

                return true;
            }
            catch
            {
                _createWithProgress = true;
                return false;
            }
        }

        /// <summary>
        /// Copies an image of a path to a destination path in the size, which was configured in the options.
        /// </summary>
        /// <param name="originalImagePath">Path of the original image.</param>
        /// <param name="destinationImagePath">Path of the destination with the image name.</param>
        private void CopyImageInSize(string originalImagePath, string destinationImagePath)
        {
            try
            {
                //if original sizes must be used just copy the image to destination and end
                if (Configuration.UseOriginalSizes)
                {
                    File.Copy(originalImagePath, destinationImagePath);
                }
                else
                {
                    Image img = Image.FromFile(originalImagePath);

                    //handle image size

                    //compute differences to get the resizePercentage
                    int widthDiff = img.Width - Configuration.ImageWidth;
                    int heightdiff = img.Height - Configuration.ImageHeight;
                    double resizePercentage = 0;
                    bool isToHandle = true;

                    if (widthDiff > 0)
                    {
                        if (heightdiff > 0)
                        {
                            if (heightdiff > widthDiff)
                            {
                                resizePercentage = (double)Configuration.ImageHeight / img.Height;
                            }
                            else
                            {
                                resizePercentage = (double)Configuration.ImageWidth / img.Width;
                            }
                        }
                        else
                        {
                            resizePercentage = (double)Configuration.ImageWidth / img.Width;
                        }
                    }
                    else if (heightdiff > 0)
                    {
                        resizePercentage = (double)Configuration.ImageHeight / img.Height;
                    }
                    else
                    {
                        //the image is not bigger as configured size
                        File.Copy(originalImagePath, destinationImagePath);
                        isToHandle = false;
                    }

                    if (isToHandle)
                    {
                        //get the height and width of the image
                        int originalW = img.Width;
                        int originalH = img.Height;

                        //get the new size based on the percentage change
                        int resizedW = (int)(originalW * resizePercentage);
                        int resizedH = (int)(originalH * resizePercentage);

                        //create a new Bitmap the size of the new image
                        Bitmap bmp = new Bitmap(resizedW, resizedH);

                        //create a new graphic from the Bitmap
                        Graphics graphic = Graphics.FromImage((Image)bmp);
                        graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;

                        //draw the newly resized image
                        graphic.DrawImage(img, 0, 0, resizedW, resizedH);

                        //dispose and free up the resources
                        graphic.Dispose();

                        //collect garbage and wait 3ms
                        GC.Collect();
                        GC.WaitForFullGCComplete(3);

                        //copy the image to destination
                        bmp.Save(destinationImagePath, ImageFormat.Jpeg);
                    }
                }

                if (_createWithProgress && _progressBar != null)
                {
                    _progressBar.Increment(1);
                }
            }
            catch(InsufficientMemoryException)
            {
                //out of memory, wait 1sec
                GC.Collect();
                GC.WaitForFullGCComplete(1000);
            }
        }
    }
}
